<!-- components/socket-wrapper.vue -->
<template>
	<view>
		<!-- 默认显示连接状态 -->
		<slot :connected="isConnected" :status="statusText">
			<text v-if="showStatus" :style="statusStyle">{{ statusText }}</text>
		</slot>
	</view>
</template>

<script>
	const DEFAULT_HEARTBEAT_INTERVAL = 30000 // 默认心跳间隔30秒
	const DEFAULT_RECONNECT_INTERVAL = 5000 // 默认重连间隔5秒

	export default {
		name: 'socket-wrapper',
		props: {
			url: {
				type: String,
				required: true
			},
			autoConnect: {
				type: Boolean,
				default: true
			},
			reconnectLimit: {
				type: Number,
				default: 5
			},
			reconnectInterval: {
				type: Number,
				default: DEFAULT_RECONNECT_INTERVAL
			},
			showStatus: {
				type: Boolean,
				default: false
			},
			heartbeat: {
				type: [Object, Boolean],
				default: false,
				validator: value => {
					if (!value || value === false) return true
					return value.interval && value.message
				}
			},
			debug: {
				type: Boolean,
				default: false
			}
		},
		data() {
			return {
				socketTask: null,
				isConnected: false,
				reconnectTimes: 0,
				reconnectTimer: null,
				heartbeatTimer: null,
				lastError: null,
				listeners: {
					message: [],
					error: [],
					open: [],
					close: []
				}
			}
		},
		computed: {
			statusText() {
				return this.isConnected ? '已连接' : this.lastError ? `连接失败: ${this.lastError.message}` : '未连接'
			},
			statusStyle() {
				return {
					color: this.isConnected ? '#4cd964' : '#dd524d',
					fontSize: '14px',
					padding: '4px 8px'
				}
			}
		},
		watch: {
			url(newVal, oldVal) {
				if (newVal !== oldVal && this.isConnected) {
					this._debug('URL变化，重新连接')
					this.reconnect()
				}
			}
		},
		created() {
			if (this.autoConnect) {
				this.connect()
			}
		},
		beforeDestroy() {
			this.disconnect()
		},
		methods: {
			// ========== 公开方法 ==========

			/**
			 * 连接WebSocket
			 * @returns {Promise<void>}
			 */
			connect() {
				return new Promise((resolve, reject) => {
					if (this.isConnected) {
						resolve()
						return
					}

					this._cleanup()

					this._debug('正在创建WebSocket连接...')
					this.socketTask = uni.connectSocket({
						url: this.url,
						complete: () => {
							this._setupEventListeners(resolve, reject)
						}
					})
				})
			},

			/**
			 * 断开WebSocket连接
			 */
			disconnect() {
				this.reconnectTimes = 5
				this._debug('主动断开连接')
				this._cleanup()
				this.$emit('close')
			},

			/**
			 * 重新连接
			 * @returns {Promise<void>}
			 */
			reconnect() {
				return this.disconnect().then(() => this.connect())
			},

			/**
			 * 发送数据（支持多种格式）
			 * @param {string|ArrayBuffer|number[]} data - 要发送的数据
			 * @returns {Promise<void>}
			 */
			send(data) {
				return new Promise((resolve, reject) => {
					if (!this.isConnected) {
						const err = new Error('WebSocket未连接')
						this._handleError(err)
						reject(err)
						return
					}

					try {
						const normalizedData = this._normalizeSendData(data)
						this._debug('发送数据:', normalizedData)

						this.socketTask.send({
							data: normalizedData,
							success: () => resolve(),
							fail: (err) => {
								this._handleError(err)
								reject(err)
							}
						})
					} catch (err) {
						this._handleError(err)
						reject(err)
					}
				})
			},

			/**
			 * 发送16进制字符串
			 * @param {string} hexStr - 16进制字符串，如 "A1B2C3D4" 或 "A1 B2 C3 D4"
			 * @returns {Promise<void>}
			 */
			sendHex(hexStr) {
				return this.send(hexStr)
			},

			/**
			 * 发送字节数组
			 * @param {number[]} byteArray - 字节数组，如 [0xA1, 0xB2, 0xC3, 0xD4]
			 * @returns {Promise<void>}
			 */
			sendBytes(byteArray) {
				return this.send(byteArray)
			},

			/**
			 * 添加事件监听
			 * @param {string} event - 事件类型: 'message' | 'error' | 'open' | 'close'
			 * @param {Function} callback - 回调函数
			 * @returns {this}
			 */
			on(event, callback) {
				if (this.listeners[event] && typeof callback === 'function') {
					this.listeners[event].push(callback)
				}
				return this
			},

			/**
			 * 移除事件监听
			 * @param {string} event - 事件类型
			 * @param {Function} callback - 要移除的回调函数
			 * @returns {this}
			 */
			off(event, callback) {
				if (this.listeners[event]) {
					this.listeners[event] = this.listeners[event].filter(cb => cb !== callback)
				}
				return this
			},

			/**
			 * 将ArrayBuffer转换为16进制字符串
			 * @param {ArrayBuffer} buffer 
			 * @param {boolean} [withSpace=true] - 是否用空格分隔字节
			 * @returns {string}
			 */
			bufferToHex(buffer, withSpace = true) {
				if (!(buffer instanceof ArrayBuffer)) {
					throw new Error('参数必须是ArrayBuffer')
				}

				const view = new Uint8Array(buffer)
				let hexStr = ''

				for (let i = 0; i < view.length; i++) {
					const hex = view[i].toString(16).padStart(2, '0').toUpperCase()
					hexStr += hex + (withSpace && i < view.length - 1 ? ' ' : '')
				}

				return hexStr
			},

			/**
			 * 将16进制字符串转换为ArrayBuffer
			 * @param {string} hexStr 
			 * @returns {ArrayBuffer}
			 */
			hexToBuffer(hexStr) {
				console.log('hexStr-------',hexStr);
				return this._hexStringToArrayBuffer(hexStr)
			},

			// ========== 内部方法 ==========

			_setupEventListeners(resolve, reject) {
				// 监听打开事件
				this.socketTask.onOpen(() => {
					this._debug('WebSocket连接已打开')
					this.isConnected = true
					this.reconnectTimes = 0
					this.lastError = null
					this._startHeartbeat()
					this.$emit('open')
					resolve()
				})

				// 监听错误事件
				this.socketTask.onError((err) => {
					this._debug('WebSocket错误:', err)
					this._handleError(err)
					reject(err)
				})

				// 监听关闭事件
				this.socketTask.onClose(() => {
					this._debug('WebSocket连接已关闭')
					this._handleClose()
				})

				// 监听消息事件
				this.socketTask.onMessage((res) => {
					try {
						this._debug('收到原始数据:', res.data)
						const data = this._normalizeReceivedData(res.data)
						this.$emit('message', data)
						this._triggerEvent('message', data)
					} catch (err) {
						console.log('监听错误',err);
						this._handleError(err)
					}
				})
			},

			_normalizeSendData(data) {
				if (data == 'heartCheck') {
					return 'heartCheck'
				}
				if (typeof data === 'string') {
					// 16进制字符串
					return this._hexStringToArrayBuffer(data)
				} else if (data instanceof ArrayBuffer) {
					// 已经是ArrayBuffer
					return data
				} else if (Array.isArray(data)) {
					// 字节数组
					return new Uint8Array(data).buffer
				}
				throw new Error('不支持的数据格式，请提供16进制字符串、ArrayBuffer或字节数组')
			},

			_normalizeReceivedData(data) {
				console.log('---------',typeof data);
				if (typeof data === 'string') {
					// 字符串可能是16进制格式
					return {
						raw: data,
						buffer: this._hexStringToArrayBuffer(data),
						type: 'string'
					}
				} else if (data instanceof ArrayBuffer) {
					// ArrayBuffer
					return {
						raw: data,
						buffer: data,
						type: 'buffer'
					}
				}
				throw new Error('未知的接收数据类型')
			},

			_hexStringToArrayBuffer(hexStr) {
				// 移除所有空格和换行
				hexStr = hexStr.replace(/\s/g, '')

				// 验证是否为有效的16进制字符串
				if (!/^[0-9a-fA-F]*$/.test(hexStr)) {
					throw new Error('无效的16进制字符串')
				}

				// 确保长度为偶数
				if (hexStr.length % 2 !== 0) {
					throw new Error('16进制字符串长度必须为偶数')
				}

				const buffer = new ArrayBuffer(hexStr.length / 2)
				const view = new Uint8Array(buffer)

				for (let i = 0, j = 0; i < hexStr.length; i += 2, j++) {
					view[j] = parseInt(hexStr.substr(i, 2), 16)
				}

				return buffer
			},

			_handleError(err) {
				this._debug('处理错误:', err)
				this.isConnected = false
				this.lastError = err
				this.$emit('error', err)
				this._triggerEvent('error', err)
				this._startReconnect()
			},

			_handleClose() {
				this.isConnected = false
				this.$emit('close')
				this._triggerEvent('close')
				this._startReconnect()
			},

			_startReconnect() {
				if (this.reconnectTimes >= this.reconnectLimit) {
					this._debug(`已达到最大重连次数 ${this.reconnectLimit}，停止重连`)
					return
				}

				clearTimeout(this.reconnectTimer)
				this.reconnectTimes++

				this._debug(`尝试第 ${this.reconnectTimes} 次重连...`)
				this.reconnectTimer = setTimeout(() => {
					this.connect().catch(() => {
						this._startReconnect()
					})
				}, this.reconnectInterval)
			},

			_startHeartbeat() {
				if (!this.heartbeat) return

				const interval = this.heartbeat.interval || DEFAULT_HEARTBEAT_INTERVAL
				// const message = this.heartbeat.message
				const message = 'heartCheck'

				clearInterval(this.heartbeatTimer)
				this._sendHeartbeat(message) // 立即发送一次

				this.heartbeatTimer = setInterval(() => {
					if (this.isConnected) {
						this._sendHeartbeat(message)
					}
				}, interval)
			},

			async _sendHeartbeat(message) {
				try {
					await this.send(message)
					this._debug('心跳发送成功', message)
				} catch (err) {
					this._debug('心跳发送失败:', err)
				}
			},

			_triggerEvent(event, ...args) {
				if (this.listeners[event]) {
					this.listeners[event].forEach(cb => cb(...args))
				}
			},

			_cleanup() {
				this._debug('清理资源...')
				clearTimeout(this.reconnectTimer)
				clearInterval(this.heartbeatTimer)

				if (this.socketTask) {
					this.socketTask.close()
					this.socketTask = null
				}

				this.isConnected = false
			},

			_debug(...args) {
				if (this.debug) {
					console.log('[SocketWrapper]', ...args)
				}
			}
		}
	}
	// 组件特性
	// 多格式支持：

	// 支持16进制字符串（如 "A1B2C3D4" 或 "A1 B2 C3 D4"）

	// 支持字节数组（如 [0xA1, 0xB2, 0xC3, 0xD4]）

	// 支持直接发送ArrayBuffer

	// 完善的事件系统：

	// open - 连接成功

	// close - 连接关闭

	// message - 收到消息（自动处理16进制和二进制）

	// error - 错误事件

	// 连接管理：

	// 自动重连机制（可配置重试次数和间隔）

	// 心跳检测（保持连接活跃）

	// 手动连接/断开控制

	// 实用工具方法：

	// bufferToHex - ArrayBuffer转16进制字符串

	// hexToBuffer - 16进制字符串转ArrayBuffer

	// 链式事件监听（on/off方法）

	// 调试支持：

	// 内置调试日志（通过debug属性开启）

	// 详细的错误处理

	// UI集成：

	// 可显示连接状态

	// 支持插槽自定义UI

	// 使用建议
	// 数据格式：与后端约定统一的16进制数据格式（是否包含空格分隔）

	// 错误处理：始终监听error事件并处理可能的连接问题

	// 大文件传输：对于大文件，考虑分片传输机制

	// 性能优化：在不需要时关闭调试模式（debug: false）

	// 生命周期：在页面/组件销毁时自动断开连接

	// 这个组件提供了全面的WebSocket通信解决方案，特别适合需要处理16进制数据的物联网、硬件通信等场景。
</script>

<style scoped>
	/* 可自定义样式 */
</style>